package com.radar.io.cinrad;

import cn.hutool.core.date.DateUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.radar.algorithm.MathCommon;
import com.radar.common.*;
import com.radar.exception.RadarCaclError;
import com.radar.exception.RadarDecodeError;
import com.radar.exception.RadarVisualizeError;
import com.radar.io.CinradRadar;
import com.radar.io.standard.RadarStandard;
import com.radar.utils.BitConverter;
import com.radar.visualize.RasterMap;
import ucar.ma2.ArrayByte;
import ucar.ma2.ArrayDouble;
import ucar.ma2.DataType;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.Variable;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * @author: fy-wbj
 * @description: 382 波段 新的雷达 脑子有B
 * @date: 2020/10/10
 */
public class Radar382_A extends BaseCinrad {

    private double[] ele;

    private double[][] azi;

    private double reso = 300;

    private Date scanEndTime;

    double maxL;

    static Log log = LogFactory.get();

    double lat;

    double lon;


    public Radar382_A(String file, CinradType type, String code) {
        super(file, type, code);
    }

    public void parse(byte[] bytes) throws RadarDecodeError {
        try {
            int index = 0;
            byte[] bytdata = new byte[1266];
            System.arraycopy(bytes, index, bytdata, 0, bytdata.length);
            index += 1266;
            RealFile header = new RealFile();
            header.parse(bytdata);
            if (header.realObserSec.iProductNumber != 2) {
                log.error("382_A iProductNumber is not 2");
                return;
            }
            GregorianCalendar gc = new GregorianCalendar();
            String format = header.realObserSec.iGuanCeKaiShiNian + "-" + (header.realObserSec.iGuanCeKaiShiYue < 10 ? "0" + header.realObserSec.iGuanCeKaiShiYue : header.realObserSec.iGuanCeKaiShiYue) + "-" + (header.realObserSec.iGuanCeKaiShiRi < 10 ? "0" + header.realObserSec.iGuanCeKaiShiRi : header.realObserSec.iGuanCeKaiShiRi);
            Date date = DateUtil.parse(format, DateConstants.DATE_FORMAT_YY_MM_DD);
            gc.setTime(date);
            gc.set(GregorianCalendar.HOUR_OF_DAY, header.realObserSec.iGuanCeKaiShiShi);
            gc.set(GregorianCalendar.MINUTE, header.realObserSec.iGuanCeKaiShiFen);
            gc.set(GregorianCalendar.SECOND, header.realObserSec.iGuanCeKaiShiMiao);
            lon = header.realAddrSec.LongitudeV;
            lat = header.realAddrSec.LatitudeV;
            scanEndTime = gc.getTime();
            dateTime = gc.getTime();
            int Layers = header.realObserSec.iLiTiSaoMiaoCengShu;
            int[] Records = new int[Layers];
            double[] MaxV = new double[Layers];
            int Bins = 0;
            for (int i = 0; i < Layers; i++) {
                Records[i] = header.realObserSec.LayerParam[i].iCaiYangJinXiangShu;
                Bins = header.realObserSec.LayerParam[i].usRefBinNumber;
                reso = header.realObserSec.LayerParam[i].iKuChang;
                MaxV[i] = (double) (int) header.realObserSec.LayerParam[i].iZuiDaCeSu / 100.0;
            }
            int max = Arrays.stream(Records).max().getAsInt();
            byte[][][] Z = new byte[Layers][max][Bins];
            byte[][][] V = new byte[Layers][max][Bins];
            byte[][][] W = new byte[Layers][max][Bins];
//
            ele = new double[Layers];
            azi = new double[Layers][max];
            MathCommon.fill(ele);
            MathCommon.fill(azi);

            MathCommon.fill(Z);
            MathCommon.fill(V);
            MathCommon.fill(W);
            for (int i = 0; i < Layers; i++) {
                int bidx = Records[i];

                for (int j = 0; j < bidx; j++) {
                    RadialHeader radialHeader = new RadialHeader();
                    bytdata = new byte[radialHeader.len];
                    System.arraycopy(bytes, index, bytdata, 0, bytdata.length);
                    radialHeader.parse(bytdata);
                    ele[i] = radialHeader.elevationNumber;
                    azi[i][j] = radialHeader.azimuth;
                    index += bytdata.length;
                    // header
                    byte[] z = Arrays.copyOfRange(bytes, index, index + Bins);
                    for (int k = 0; k < Bins; k++) {
                        double c = ((0xff & z[k]) - 66.0) * 0.5;
                        Z[i][j][k] = c < -10 ? -128 : MathCommon.CodeRef(c);
                    }
                    index += Bins;
                    byte[] v = Arrays.copyOfRange(bytes, index, index + Bins);
                    for (int k = 0; k < Bins; k++) {
                        double c = ((0xff & v[k]) - 129.0) * 0.5;
                        V[i][j][k] = c < -60.5 ? -128 : MathCommon.CodeV(c);
                    }
                    index += Bins;
                    byte[] w = Arrays.copyOfRange(bytes, index, index + Bins);
                    for (int k = 0; k < Bins; k++) {
                        double c = ((0xff & w[k]) - 129.0) * 0.5;
                        W[i][j][k] = c < -60.5 ? -128 : MathCommon.CodeV(c);
                    }
                    index += Bins;
                }
            }
            this.initNetcdfTemp();
            this.writeBasicData(Z, V, W);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("382 handle error");
        }
    }

    @Override
    public double[] getElevation() {
        return ele;
    }

    @Override
    public int[] getElevationIndex(ProductType type) {
        return IntStream.range(0, ele.length).toArray();
    }

    @Override
    public double[][] getAzimuth() {
        return azi;
    }

    @Override
    public double getResolution(ProductType type) {
        return reso;
    }

    @Override
    public RadarSite.RadarSiteItem getRadarSite() throws RadarDecodeError {
        return new RadarSite().new RadarSiteItem("", "", "", "", CinradType.RADAR_382, lon, lat, 0);
    }

    public void writeBasicData(byte[][][] z, byte[][][] v, byte[][][] w) throws IOException {
        int dlength = z[0][0].length;
        write.setRedefineMode(true);
        Variable v1;
        if ((v1 = this.netcdf.findVariable("distince")) == null) {
            write.addDimension(null, "distince", dlength);
            v1 = write.addVariable(null, "distince", DataType.DOUBLE, "distince");
            v1.addAttribute(new Attribute("units", "m"));
            v1.addAttribute(new Attribute("resolution", reso));
        }
        List<Dimension> dimensions = new ArrayList<>();
        dimensions.add(netcdf.findDimension("elevation"));
        dimensions.add(netcdf.findDimension("azimuth"));
        dimensions.add(netcdf.findDimension("distince"));
        Variable rVariable = ProductType.REF.writeVariable(write, netcdf, dimensions);
        Variable vVariable = ProductType.VEL.writeVariable(write, netcdf, dimensions);
        Variable wVariable = ProductType.SW.writeVariable(write, netcdf, dimensions);
        ArrayDouble rDistances = new ArrayDouble.D1(dlength);
        IntStream.range(0, dlength).forEach(i -> rDistances.setDouble(i, (i + 1) * reso));
        ArrayByte rArray = MathCommon.toByteArray(z);
        ArrayByte wArray = MathCommon.toByteArray(w);
        ArrayByte vArray = MathCommon.toByteArray(v);
        rVariable.setCachedData(rArray);
        vVariable.setCachedData(vArray);
        wVariable.setCachedData(wArray);
        v1.setCachedData(rDistances);
    }

    public static double[][] convert(double[][] data, ProductType productType){
        double[][] d = new double[data.length][data[0].length];
        for (int i = 0 ; i < d.length ; i ++) {
            for (int j = 0 ; j < d[0].length ; j ++) {
                d[i][j] = GisConstants.UNDEF;
                if (data[i][j] != GisConstants.UNDEF){
                    if (productType == ProductType.VEL || productType == ProductType.SW){
                        d[i][j] = data[i][j] * 2 + 129;
                    }else {
                        d[i][j] = data[i][j] * 2 + 66;
                    }
                }
            }
        }
        return d;
    }
    //region 原始数据文件

    //站址段 160
    public class RealAddrSec {

        //!< 文件头长度 ** 【2】
        short HeadLen;
        //!< 国名  【20】
        String Nation;
        //!< 省名  【20】
        String State;
        //!< 区名  【20】
        String Station;
        //!< 区站名  【20】
        String SectionSta;
        //!< 文件格式版本号 [4 - 7]存放数据来源 【20】
        String Type;
        //!< 格式E15度32'12"  【20】
        String Longitude;
        //!< 格式N35度30'15"  【20】
        String Latitude;
        //!< 天线所在径度的数值表示，单位取1／1000度(东经为整，西径为负)   **  【4】
        double LongitudeV;
        //!< 天线所在纬度的数值表示，单位取1／1000度(北纬为正，南纬为负)   **  【4】
        double LatitudeV;
        //!< 天线所在的海拔高度，以mm为单位                                **  【4】
        int Height;
        //!< 测站四周地物的最大仰角(以百分之一度为单位)  【2】
        int Elevation;
        //!< 测站的最佳观测仰角(地物回波强度10dbz)，以百分之一度为单位  【2】
        int Elevation1;
        //!< 站点编号  【2】
        int Category;

        int size = 160;

        public void parse(byte[] b) {
            int index = 0;
            HeadLen = BitConverter.toInt16(b, index);
            index += 2;
            Nation = new String(Arrays.copyOfRange(b, index, index + 20));
            index += 20;
            State = new String(Arrays.copyOfRange(b, index, index + 20));
            index += 20;
            Station = new String(Arrays.copyOfRange(b, index, index + 20));
            index += 20;
            SectionSta = new String(Arrays.copyOfRange(b, index, index + 20));
            index += 20;
            Type = new String(Arrays.copyOfRange(b, index, index + 20));
            index += 20;
            Longitude = new String(Arrays.copyOfRange(b, index, index + 20));
            index += 20;
            Latitude = new String(Arrays.copyOfRange(b, index, index + 20));
            index += 20;
            LongitudeV = BitConverter.toInt32(b, index) / 360000.0;
            index += 4;
            LatitudeV = BitConverter.toInt32(b, index) / 360000.0;
            index += 4;
            Height = BitConverter.toInt32(b, index);
            index += 4;
            Elevation = BitConverter.toInt16(b, index);
            index += 2;
            Elevation1 = BitConverter.toInt16(b, index);
            index += 2;
            Category = BitConverter.toInt16(b, index);
            index += 2;
        }
    }

    //性能参数段 40
    public class RealCapabilitySec {
        int antennaG;                //天线增益,以0.01dB为计数单位
        int beamH;            //垂直波束宽度 单位取百分之一度
        int beamL;            //!< 水平波束宽度  单位取百分之一度     **  【2】
        int polarizations;     //极化状况 0=水平，  1=垂直， 2=双偏振，3=圆偏振，4=其他
        int sidelobe;              //!< 第一旁瓣电平，取绝对值（单位取百分之一dB）  【2】
        int wavelength;        //!< 波长，以微米为单位          **  【2】
        int power;            //!< 雷达峰值功率，以瓦为单位  【4】
        int logA;       //!< 对数接收机动态范围，以百分之一dB为单位  【2】
        int LineA;      //!< 线性接收机动态范围，以百分之一dB为单位  【2】
        int AGCP;       //!< AGC延迟量，以微秒为单位  【2】
        int iDuShuZuiXiaoGongLv;    //!< 对数接收机最小可测功率，以百分之一dbmw为单位  【2】
        int iXianXingZuiXiaoGongLv;   //!< 线性接收机最小可测功率，以百分之一dbmw为单位  【2】
        int iZaoShengYuZhi;    //!< 噪声消除的量化阀值（0-255）  【2】
        int iDuoPoLeXiao;     //!< 多普勒杂波消除阀值，单位0.01db  【2】
        int sQIT;       //!< SQI阀值（0-1）：实际值乘1000  【2】
        int iSuDuChuLiFangShi; /*! 速度处理方式   【2】
                                             0:无速度处理；
                                             1:PPI；
                                             2:FFT；
                                            */
        int iDiWuChuLiFangShi; /*! 地物处理方式  【2】
                                             - 0:无地物处理；
                                             - 1:地物杂波图扣除法；
                                             - 2:滤波器处理；
                                             - 3:滤波器＋地物杂波图法；
                                             - 4:谱分析法
                                            */
        int iQiangDuTongDao;   /*! 强度估算采用的通道  【2】
                                             - 1:对数;
                                             - 2:线性
                                            */
        int iJuLiDingZheng;    //!< 【2】
        int size = 40;

        public void parse(byte[] b) {
            int index = 0;
            antennaG = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            beamH = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            beamL = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            polarizations = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            sidelobe = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            wavelength = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            power = BitConverter.toInt32(b, index);
            index += 4;
            logA = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            LineA = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            AGCP = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            iDuShuZuiXiaoGongLv = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            iXianXingZuiXiaoGongLv = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            iZaoShengYuZhi = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            iDuoPoLeXiao = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            sQIT = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            iSuDuChuLiFangShi = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            iDiWuChuLiFangShi = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            iQiangDuTongDao = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            iJuLiDingZheng = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
        }

    }

    //观测参数段 1066
    public class RealObserSec {
        int iProductNumber;	/*! 产品编号 <0>        **  【2】
                                             - 0:PPI1
                                             - 1:RHI
                                             - 2:立体扫描
                                             - 3.反射率
                                             - 4.速度
                                             - 5.谱宽
                                            */
        int iLiTiSaoMiaoCengShu;    //!< 立体扫描层数（PHI扫描填零，PPI扫描时填1）<2> **  【2】
        int iGuanCeKaiShiNian;        //!< 观测开始时间 年（4位数如2000）	<4>  【2】
        int iGuanCeKaiShiYue;        //!< 观测开始时间 月（01-12）<6>  【2】
        int iGuanCeKaiShiRi;        //!< 观测开始时间 日（01-31）  【2】
        int iGuanCeKaiShiShi;        //!< 观测开始时间 小时（00-23）  【2】
        int iGuanCeKaiShiFen;        //!< 观测开始时间 分（00-59）  【2】
        int iGuanCeKaiShiMiao;        //!< 观测开始时间 秒（00-59）  【2】
        int iKaiShiGPSShiJian;        //!< 开始GPS时间(无GPS填零)  【4】
        int iDingBiaoQingKuang;	/*! 定标情况  【2】
                                                 - 0:没有定标
                                                 - 1:自动定标
                                                 - 2:一周内人工定标
                                                 - 3:一月内人工定标
                                                */
        int iQianDuJiFenCiShu;        //!< 强度积分次数  【2】
        int iSuDuChuLiShu;            //!< 速度处理样本数  【2】
        LayerParam[] LayerParam = new LayerParam[30];

        byte cShuJuPaiLieFangShi;		/*! 一个径向中的数据排列方式      ** 【1】
                                        - 11按库排列：库中先速度后强度（两要素）
                                        - 12按库排列：库中先强度后速度（两要素）
                                        - 13按库排列：库中先速度后谱宽（两要素）
                                        - 14按库排列：库中先速度后强度再谱宽
                                        - 15按库排列：库中先强度后速度再谱宽
                                        - 21按要素排列：单强度,地物滤波( ConR )
                                        - 22按要素排列：单速度( V )
                                        - 23按要素排列：先强度后速度( ConR + V )
                                        - 24按要素排列：先速度后强度( V + ConR )
                                        - 25按要素排列：先速度后谱宽( V + W )
                                        - 26按要素排列：先速度后强度再谱宽( V + ConR + W )
                                        - 27按要素排列：先强度后速度再谱宽( ConR + V + W )
                                        - 31 346-S, 按要素排列:  ConR
                                        - 32 346-S, 按要素排列:  R
                                        - 33 346-S, 按要素排列:  ConR + R
                                        - 34 346-S, 按要素排列:  ConR + V + W
                                        - 35 346-S, 按要素排列:  R + V + W
                                        - 36 346-S, 按要素排列:  ConR + R + V + W
                                    */
        byte cQiangDuZiJieShu;	/*! 一个强度数据占用的字节数，百位数表示  【1】
                                     - 是否有符号位，
                                      -#  Oxx：表示不无符号位；
                                      -# 1xx：表示有符号位
                                    */
        byte cSuDuZiJieShu;	/*! 一个速度数据占用的字节数，百位数表示是否有符号位  【1】
                                     - Oxx：表示无符号位；
                                     - 1xx：表示有符号位。
                                    */
        byte cPuKuanZiJieShu;	/*! 一个谱宽数据占用的字节f数，百位数表示是否有符号位  【1】
                                     - 0:表示不无符号位；
                                     - 1:表示有符号位。
                                    */
        int iQiangDuWuHuiBoMa;    //!< 强度：无回波的代表码        **  【2】
        int iSuDuWuHuiBoMa;        //!< 速度：无回波的代表码  【2】
        int iPuKuanWuHuiBoMa;        //!< 谱宽：无回波的代表码  【2】
        int iQiangDuZengLiang;    //!< 数据中的强度最小增量01000  【2】
        int iSuDuZengLiang;        //!< 数据中的速度最小增量*1000  【2】
        int iPuKuanZengLiang;        //!< 数据中的谱宽最小增量*1000  【2】
        int iQiangDu0;        //!< 强度：如果用无符号类型数表示，填写代表零的数值  【2】
        int iSuDu0;        //!< 速度：如果用无符号类型数表示，填写代表零的数值  【2】
        int iPuKuan0;        //!< 谱宽：如果用无符号类型数表示，填写代表零的数值  【2】
        int iGuanCeJieSuNian;    //!< 观测结束时间，年(四位数)  【2】
        int iGuanCeJieSuYue;    //!< 观测结束时间，月(1-12)  【2】
        int iGuanCeJieSuRi;    //!< 观测结束时间，日(1-31)  【2】
        int iGuanCeJieSuShi;    //!< 观测结束时间，时(00-23)  【2】
        int iGuanCeJieSuFen;    //!< 观测结束时间，分(00-59)  【2】
        int iGuanCeJieSuMiao;    //!< 观测结束时间，秒(00-59)  【2】
        long iJieSuGPSShiJian;    //!< GPS时间(无GPS填零)  【4】
        int iJieGou1DaXiao;    //!< 应写注结构(1)数组的大小。 【2】

        public void parse(byte[] b) {
            int index = 0;
            iProductNumber = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iLiTiSaoMiaoCengShu = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iGuanCeKaiShiNian = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iGuanCeKaiShiYue = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iGuanCeKaiShiRi = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iGuanCeKaiShiShi = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iGuanCeKaiShiFen = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iGuanCeKaiShiMiao = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iKaiShiGPSShiJian = BitConverter.toInt32(b, index);
            index += 4;
            iDingBiaoQingKuang = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iQianDuJiFenCiShu = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iSuDuChuLiShu = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            IntStream.range(0, 30).forEach(i -> LayerParam[i] = new LayerParam());
            AtomicInteger finalIndex = new AtomicInteger(index);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].id = BitConverter.toInt32(b, finalIndex.intValue() + i * 4));
            finalIndex.set(finalIndex.intValue() + 120);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].cGuanCeYaoSu = b[finalIndex.intValue() + i]);
            finalIndex.set(finalIndex.intValue() + 30);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].cSuDuTuiMoFu = b[finalIndex.intValue() + i]);
            finalIndex.set(finalIndex.intValue() + 30);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].iYiMaiChongChongPin = Short.toUnsignedInt(BitConverter.toInt16(b, finalIndex.intValue() + i * 2)));
            finalIndex.set(finalIndex.intValue() + 60);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].iErMaiChongChongPin = Short.toUnsignedInt(BitConverter.toInt16(b, finalIndex.intValue() + i * 2)));
            finalIndex.set(finalIndex.intValue() + 60);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].iMaiChongKuanDu = Short.toUnsignedInt(BitConverter.toInt16(b, finalIndex.intValue() + i * 2)));
            finalIndex.set(finalIndex.intValue() + 60);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].iZuiDaCeSu = Short.toUnsignedInt(BitConverter.toInt16(b, finalIndex.intValue() + i * 2)));
            finalIndex.set(finalIndex.intValue() + 60);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].usRefBinNumber = Short.toUnsignedInt(BitConverter.toInt16(b, finalIndex.intValue() + i * 2)));
            finalIndex.set(finalIndex.intValue() + 60);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].iCaiYangJinXiangShu = Short.toUnsignedInt(BitConverter.toInt16(b, finalIndex.intValue() + i * 2)));
            finalIndex.set(finalIndex.intValue() + 60);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].iJinXiangKuShu = Short.toUnsignedInt(BitConverter.toInt16(b, finalIndex.intValue() + i * 2)));
            finalIndex.set(finalIndex.intValue() + 60);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].iKuChang = Short.toUnsignedInt(BitConverter.toInt16(b, finalIndex.intValue() + i * 2)));
            finalIndex.set(finalIndex.intValue() + 60);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].iDiYiKuJuLi = BitConverter.toInt32(b, finalIndex.intValue() + i * 4));
            finalIndex.set(finalIndex.intValue() + 120);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].iPPIKaiShiWeiZhi = Short.toUnsignedInt(BitConverter.toInt16(b, finalIndex.intValue() + i * 2)));
            finalIndex.set(finalIndex.intValue() + 60);
            IntStream.range(0, 30).forEach(i -> LayerParam[i].iYangJiao = BitConverter.toInt16(b, finalIndex.intValue() + i * 2));
            index = finalIndex.intValue() + 60;
            cShuJuPaiLieFangShi = b[index];
            index++;
            cQiangDuZiJieShu = b[index];
            index++;
            cSuDuZiJieShu = b[index];
            index++;
            cPuKuanZiJieShu = b[index];
            index++;
            iQiangDuWuHuiBoMa = BitConverter.toInt16(b, index);
            index += 2;
            iSuDuWuHuiBoMa = BitConverter.toInt16(b, index);
            index += 2;
            iPuKuanWuHuiBoMa = BitConverter.toInt16(b, index);
            index += 2;
            iQiangDuZengLiang = BitConverter.toInt16(b, index);
            index += 2;
            iSuDuZengLiang = BitConverter.toInt16(b, index);
            index += 2;
            iPuKuanZengLiang = BitConverter.toInt16(b, index);
            index += 2;
            iQiangDu0 = BitConverter.toInt16(b, index);
            index += 2;
            iSuDu0 = BitConverter.toInt16(b, index);
            index += 2;
            iPuKuan0 = BitConverter.toInt16(b, index);
            index += 2;
            iGuanCeJieSuNian = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iGuanCeJieSuYue = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iGuanCeJieSuRi = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iGuanCeJieSuShi = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iGuanCeJieSuFen = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iGuanCeJieSuMiao = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;
            iJieSuGPSShiJian = Integer.toUnsignedLong(BitConverter.toInt32(b, index));
            index += 4;
            iJieGou1DaXiao = Short.toUnsignedInt(BitConverter.toInt16(b, index));
            index += 2;

        }
    }

    public class LayerParam {
        int id;
        byte cGuanCeYaoSu;
        int cSuDuTuiMoFu;
        int iYiMaiChongChongPin;
        int iErMaiChongChongPin;
        int iMaiChongKuanDu;
        int iZuiDaCeSu;
        int usRefBinNumber;
        int iCaiYangJinXiangShu;
        int iJinXiangKuShu;
        int iKuChang;
        int iDiYiKuJuLi;
        int iPPIKaiShiWeiZhi;
        int iYangJiao;
    }

    public class RealFile {
        // 160
        public RealAddrSec realAddrSec;
        // 40
        public RealCapabilitySec realCapabilitySec;
        // 1066
        public RealObserSec realObserSec;

        public void parse(byte[] b) {
            int index = 0;
            byte[] bytdata = new byte[160];
            System.arraycopy(b, index, bytdata, 0, bytdata.length);
            realAddrSec = new RealAddrSec();
            realAddrSec.parse(bytdata);
            index += 160;

            bytdata = new byte[40];
            System.arraycopy(b, index, bytdata, 0, bytdata.length);
            realCapabilitySec = new RealCapabilitySec();
            realCapabilitySec.parse(bytdata);
            index += 40;

            bytdata = new byte[1066];
            System.arraycopy(b, index, bytdata, 0, bytdata.length);
            realObserSec = new RealObserSec();
            realObserSec.parse(bytdata);
            index += 40;
//            bytdata = new byte[516];
//            System.arraycopy(b, index, bytdata, 0, bytdata.length);
//            radarObservationInfo = new RadarObservationParamA();
//            radarObservationInfo.parse(bytdata);
//            index += 516;
//            Reserved = new byte[24];
//            System.arraycopy(b, index, Reserved, 0, 24);
//            index += 24;
        }
    }

    public class RadialHeader {
        private int len = 64;
        private int radialState;
        private int spotBlank;
        private int seqNumber;
        private int radialNumber;
        private int elevationNumber;
        private double azimuth;
        private double elevation;
        private int seconds;
        private int microseconds;
        private int dataLength;
        private int momentNumber;
        private int res5;
        private int horiEstNoise;
        private int vertEstNoise;
        private String zipType;
        private String res6;

        public void parse(byte[] b) throws Exception {
            int index = 0;
//            radialState = BitConverter.toInt32(b, index);
//            index += 4;
//            spotBlank = BitConverter.toInt32(b, index);
//            index += 4;
//            seqNumber = BitConverter.toInt32(b, index);
//            index += 4;
//            radialNumber = BitConverter.toInt32(b, index);
//            index += 4;
//            elevationNumber = BitConverter.toInt32(b, index);
//            index += 4;
            index += 4;
            azimuth = Short.toUnsignedInt(BitConverter.toInt16(b, index)) / 100.0;
            index += 2;
            elevation = Short.toUnsignedInt(BitConverter.toInt16(b, index)) / 100.0;
            index += 2;

//            seconds = BitConverter.toInt32(b, index);
//            index += 4;
//            microseconds = BitConverter.toInt32(b, index);
//            index += 4;
//            dataLength = BitConverter.toInt32(b, index);
//            index += 4;
//            momentNumber = BitConverter.toInt32(b, index);
//            index += 4;
//
//            res5 = (BitConverter.toInt16(b, index) & 0x0FFFF);
//            index += 2;
//            horiEstNoise = (BitConverter.toInt16(b, index) & 0x0FFFF);
//            index += 2;
//            vertEstNoise = (BitConverter.toInt16(b, index) & 0x0FFFF);
//            index += 2;
//            zipType = BitConverter.toString(Arrays.copyOfRange(b, index, index + 1));
//            index += 1;
//            zipType = zipType.replace("\u0000", "");
//            res6 = BitConverter.toString(Arrays.copyOfRange(b, index, index + 13));
//            index += 13;
        }
    }

    public class RadarObservationParamA {
        public byte SType;
        public byte WeatherMode;
        public int SYear;
        public byte SMonth;
        public byte SDay;
        public byte SHour;
        public byte SMinute;
        public byte SSecond;
        public byte Calibration;
        public byte LayerNum;
        // 19
        public LayerParamA[] LayerParam;
        public int RHIA;
        public short RHIL;
        public short RHIH;
        public byte[] Reserved;

        public void parse(byte[] b) {
            int index = 0;
            SType = b[index];
            index++;
            WeatherMode = b[index];
            index++;
            SYear = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            SMonth = b[index];
            index++;
            SDay = b[index];
            index++;
            SHour = b[index];
            index++;
            SMinute = b[index];
            index++;
            SSecond = b[index];
            index++;
            Calibration = b[index];
            index++;
            LayerNum = b[index];
            index++;
            LayerParam = new LayerParamA[25];
            for (int i = 0; i < LayerParam.length; i++) {
                byte[] bytdata = new byte[19];
                System.arraycopy(b, index, bytdata, 0, bytdata.length);
                LayerParam[i] = new LayerParamA();
                LayerParam[i].parse(bytdata);
                index += 19;
            }
            RHIA = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            RHIL = BitConverter.toInt16(b, index);
            index += 2;
            RHIH = BitConverter.toInt16(b, index);
            index += 2;
            Reserved = new byte[24];
            System.arraycopy(b, index, Reserved, 0, 24);
        }
    }

    public class LayerParamA {
        public byte Ambiguousp;
        public int PRF1;
        public int PRF2;
        public byte Filter;
        public int PluseW;
        public int MaxV;
        public int MaxL;
        public int BinWidth;
        public int BinNumber;
        public int RecodeNumber;
        public int DataForm;

        public void parse(byte[] b) {
            int index = 0;
            Ambiguousp = b[index];
            index++;
            PRF1 = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            PRF2 = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            Filter = b[index];
            index++;
            PluseW = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            MaxV = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            MaxL = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            BinWidth = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            BinNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            RecodeNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            DataForm = (0xff & b[index]);
            index++;
        }
    }

    public class lineDataBlock {
        public short Elevation;
        public int Azimuth;
        public String Longitude;
        public String Latitude;
        public float Vs;
        public float Hs;
        public int Course;
        public int Nv;
        public byte[] CorZ;
        public byte[] UnZ;
        public int[] V;
        public byte[] W;

        public void parse(byte[] b) {
            int index = 0;
            Elevation = BitConverter.toInt16(b, index);
            index += 2;
            Azimuth = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            Longitude = new String(Arrays.copyOfRange(b, index, index + 16));
            index += 16;
            Latitude = new String(Arrays.copyOfRange(b, index, index + 16));
            index += 16;
            Vs = BitConverter.toSingle(b, index);
            index += 4;
            Hs = BitConverter.toSingle(b, index);
            index += 4;
            Course = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            Nv = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            CorZ = new byte[1024];
            System.arraycopy(b, index, CorZ, 0, CorZ.length);
            index += 1024;
            UnZ = new byte[1024];
            System.arraycopy(b, index, UnZ, 0, UnZ.length);
            index += 1024;
            V = new int[1024];
            byte[] bytdata = new byte[1024];
            System.arraycopy(b, index, bytdata, 0, bytdata.length);
            for (int i = 0; i < V.length; i++) {
                V[i] = (0xff & bytdata[i]);
            }
            index += 1024;
            W = new byte[1024];
            System.arraycopy(b, index, W, 0, W.length);
            index += 1024;
        }
    }
    //endregion

    public static void main(String[] args) throws RadarDecodeError, InvalidRangeException, RadarCaclError, RadarVisualizeError, IOException {
        CinradRadar cinradRadar = new CinradRadar();
        cinradRadar.parse("Z:\\tmp\\radar\\707\\data\\20231017\\VOL\\20231017_125036.00.002.005.S.bz2", CinradType.RADAR_382_A);
//        for (int i = 0 ; i < 25 ; i ++) {
//            byte[][] raw = cinradRadar.radar.getRaw(i, ProductType.REF);
//            byte[][] gridppi = Cacl.gridppi(cinradRadar.radar.getAzimuth()[i], raw, ProductType.REF);
//            BufferedImage bufferedImage = new RasterMap(cinradRadar.radar).drawSingleRaster(MathCommon.decode(gridppi,ProductType.REF),ProductType.REF);
//            ImageIO.write(bufferedImage,"png",new File("/Users/xx/DeskTop/" + i + ".png"));
//        }
        BufferedImage bufferedImage = new RasterMap(cinradRadar.radar).simplePPI(RasterMap.buildRasterInfo(ProductType.REF), 0);
//        BufferedImage bufferedImage1 = new RasterMap(cinradRadar.radar).simplePPI(RasterMap.buildRasterInfo(ProductType.REF), 10);
//
        ImageIO.write(bufferedImage, "png", new File("D://test.png"));
    }
}
